feat(mcp): OAuth 2.0 (authorization-code + PKCE) for http/sse servers#122
Merged
Conversation
Adds the last MCP §3.3 capability: connect to MCP servers that require OAuth. The SDK drives the protocol; we supply an OAuthClientProvider (mcp/oauth.ts): - McpAuthStore — persists tokens / dynamic client registration / PKCE verifier to ~/.deepcode/mcp-auth/<server>.json (auto-refresh handled by the SDK). - startLoopbackReceiver — one-shot 127.0.0.1 server that captures the ?code=/state from the browser redirect (validates state, surfaces errors). - DeepCodeOAuthProvider — PKCE client metadata (token_endpoint_auth_method 'none', authorization_code + refresh_token grants), store-backed persistence, opens the system browser, invalidateCredentials by scope. Wiring (client.ts): opt-in via `mcpServers.<name>.oauth: true` (+ optional `oauthScopes`). connectMcpServer passes the provider as the transport authProvider; on the first `UnauthorizedError` it awaits the loopback code, calls transport.finishAuth(code), and reconnects. stdio servers ignore it. Tests (+9): McpAuthStore read/patch/clear-by-scope; loopback captures the code / rejects on error / state-mismatch; provider PKCE metadata, token+verifier persistence across instances, browser-open + missing-verifier error. Core 597. Note: the live token exchange is exercised by the SDK; end-to-end against a real provider is manual (no OAuth server in CI). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
oratis
added a commit
that referenced
this pull request
May 31, 2026
OAuth 2.0 (authorization-code + PKCE) landed in #122 — the last 🔄 in the MCP section is now ✅. With it the MCP §3.3 parity line is complete. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the last MCP §3.3 capability: connecting to MCP servers that require OAuth (Linear, GitHub-hosted MCP, etc.). The SDK drives the protocol; DeepCode supplies an
OAuthClientProvider.Opt-in per server:
{ "mcpServers": { "linear": { "url": "https://mcp.linear.app/sse", "oauth": true, "oauthScopes": ["read"] } } }On first connect it opens the browser; tokens persist under
~/.deepcode/mcp-auth/<server>.jsonand auto-refresh thereafter.Changes (
packages/core/src/mcp/oauth.ts)McpAuthStore— file-backed persistence of tokens / dynamic client registration / PKCE verifier, with scopedclear()(mirrorsinvalidateCredentials).startLoopbackReceiver— a one-shot127.0.0.1:<random>/callbackserver that captures?code=/statefrom the redirect, validatesstate, and surfaces?error=.DeepCodeOAuthProvider implements OAuthClientProvider— PKCE client metadata (token_endpoint_auth_method: none,authorization_code+refresh_tokengrants), store-backedtokens/clientInformation/codeVerifier, opens the system browser (overridable),invalidateCredentials(scope).Wiring (
client.ts):connectMcpServercreates the provider (http/sse only) and passes it as the transportauthProvider. On the firstUnauthorizedError, itawaits the loopback code, callstransport.finishAuth(code), and reconnects. Addedoauth/oauthScopestoMcpServerConfig.Tests (+9,
oauth.test.ts)McpAuthStore: empty read, patch+merge persistence,clearby scope vsall.fetch→waitForCode), rejects on?error=, rejects on state mismatch.codeVerifier()throws when none saved;redirectToAuthorizationopens the URL.Core suite 597 green; full repo green via pre-commit.
Verification boundary
The persistence, loopback, metadata, and connect/retry wiring are unit-tested and follow the SDK's documented
authProvider+finishAuthcontract. The live token exchange is performed by the SDK (which has its own coverage); a full end-to-end run against a real OAuth provider is manual (no OAuth server in CI).🤖 Generated with Claude Code